<Serializable()> _
Public Class Criteria
    Implements ICriteriaComponent

    Protected mOp As Operation
    Protected mNegate As Boolean
    Protected mForceQuotes As Boolean
    Protected mSubQuery As SelectQuery

    Public Property LeftExpressionType As ExpressionType = ExpressionType.Auto
    Public Property RightExpressionType As ExpressionType = ExpressionType.Auto

    Private mLeftExpression As Object
    Public Property LeftExpression() As String
        Get
            Return mLeftExpression.ToString
        End Get
        Set(ByVal value As String)
            mLeftExpression = value
        End Set
    End Property

    Private mRightExpression As Object
    Public Property RightExpression() As String
        Get
            Return mRightExpression.ToString
        End Get
        Set(ByVal value As String)
            mRightExpression = value
        End Set
    End Property

    Public Property SubQuery() As SelectQuery
        Get
            Return mSubQuery
        End Get
        Set(ByVal value As SelectQuery)
            mSubQuery = value
        End Set
    End Property

    Public Property Op() As Operation
        Get
            Return mOp
        End Get
        Set(ByVal value As Operation)
            mOp = value
            CheckPatternAndOp()
        End Set
    End Property

    <Obsolete("Use .LeftExpression")> _
    Public Property Field() As String
        Get
            Return mLeftExpression.ToString
        End Get
        Set(ByVal value As String)
            mLeftExpression = value
        End Set
    End Property

    Public Property Negate() As Boolean
        Get
            Return mNegate
        End Get
        Set(ByVal value As Boolean)
            mNegate = value
        End Set
    End Property

    Public Property ForceQuotes() As Boolean
        Get
            Return mForceQuotes
        End Get
        Set(ByVal value As Boolean)
            mForceQuotes = value
        End Set
    End Property

    Public Sub New()
    End Sub
    Public Sub New(ByVal LeftExpression As String, ByVal Op As Operation, ByVal RightExpression As String)
        Me.New(LeftExpression, Op, RightExpression, False)
    End Sub
    Public Sub New(ByVal SubQuery As SelectQuery, ByVal Op As Operation, ByVal RightExpression As String)
        Me.New("", Op, RightExpression, False)

        If RightExpression Is Nothing Then
            Throw New ArgumentException("SubQuery may not be NULL.")
        End If

        Me.mSubQuery = SubQuery
    End Sub
    Public Sub New(ByVal LeftExpression As String, ByVal Op As Operation, ByVal RightExpression As String, ByVal ForceQuotes As Boolean)
        If RightExpression Is Nothing Then
            Throw New ArgumentException("Pattern may not be NULL.")
        End If
        If LeftExpression Is Nothing Then
            Throw New ArgumentException("Field may not be NULL.")
        End If

        mLeftExpression = LeftExpression
        mOp = Op
        mRightExpression = RightExpression
        mForceQuotes = ForceQuotes
        CheckPatternAndOp()
    End Sub

    Public Function GetText() As String Implements ICriteriaComponent.GetText
        Return GetText(QueryOptions.DefaultOptions)
    End Function

    Public Function GetText(Options As QueryOptions) As String Implements ICriteriaComponent.GetText
        CheckPatternAndOp()
        Dim Query As String = ""
        If mNegate Then Query = "NOT "

        'Dim UseQuotes As Boolean = Not IsNumeric(mPattern) AndAlso Not mPattern.ToString.StartsWith("@")
        'If mForceQuotes Then UseQuotes = True
        Dim OpString As String = ""
        Dim WildLeft As Boolean = False
        Dim WildRight As Boolean = False
        Dim SurpressPattern As Boolean = False
        Dim SpacesRequired As Boolean = False
        Select Case mOp
            Case Operation.Contains
                OpString = "LIKE"
                WildLeft = True
                WildRight = True
                SpacesRequired = True
            Case Operation.EndsWith
                OpString = "LIKE"
                WildLeft = True
                WildRight = False
                SpacesRequired = True
            Case Operation.EqualTo
                OpString = "="
            Case Operation.GreaterThen
                OpString = ">"
            Case Operation.LessThen
                OpString = "<"
            Case Operation.StartsWith
                OpString = "LIKE"
                WildLeft = False
                WildRight = True
                SpacesRequired = True
            Case Operation.NotEqualTo
                OpString = "<>"
            Case Operation.IsNULL
                OpString = "IS NULL"
                SpacesRequired = True
                SurpressPattern = True
            Case Operation.Like
                OpString = "LIKE"
                SpacesRequired = True
            Case Operation.IsNotNULL
                OpString = "IS NOT NULL"
                SurpressPattern = True
                SpacesRequired = True
            Case Operation.LessThanEqualTo
                OpString = "<="
            Case Operation.GreaterThanEqualTo
                OpString = ">="
            Case Operation.NotGreaterThan
                OpString = "!>"
            Case Operation.NotLessThan
                OpString = "!<"
        End Select

        Dim Space As String = If(Options.CompressSpaces, "", " ")
        If SpacesRequired Then
            Space = " "
        End If

        If mSubQuery IsNot Nothing Then
            Query &= "(" & mSubQuery.GetSQLText(Options) & ")" & Space
        Else
            Query &= PrepareLeftExpression(mLeftExpression.ToString, ExpressionType.Auto, False, False, Options) & Space
        End If

        If SurpressPattern Then
            Query &= OpString
        Else
            Query &= OpString & Space
            Query &= PrepareRightExpression(mRightExpression.ToString, RightExpressionType, WildLeft, WildRight, Options)
        End If

        Return Query
    End Function

    Private Function PrepareLeftExpression(ByVal Expression As String, ExpressionType As ExpressionType, WildLeft As Boolean, WildRight As Boolean, options As QueryOptions) As String
        Dim UseQuotes As Boolean
        Select Case ExpressionType
            Case QBuilder.ExpressionType.Literal
                UseQuotes = Not IsNumeric(Expression)
            Case QBuilder.ExpressionType.Auto, QBuilder.ExpressionType.Logical
                UseQuotes = False
            Case QBuilder.ExpressionType.Exact
                Return Expression
        End Select

        Return PrepareExpressionCommon(Expression, WildLeft, WildRight, UseQuotes)
    End Function

    Private Function PrepareRightExpression(ByVal Expression As String, ExpressionType As ExpressionType, WildLeft As Boolean, WildRight As Boolean, options As QueryOptions) As String
        Dim UseQuotes As Boolean

        Select Case ExpressionType
            Case QBuilder.ExpressionType.Literal
                UseQuotes = Not IsNumeric(Expression)
            Case QBuilder.ExpressionType.Auto
                If IsContainsStyleOperator(mOp) Then
                    UseQuotes = True
                Else
                    UseQuotes = Not IsNumeric(Expression) AndAlso Not DatabaseTypeHelper.LooksLikeParam(Expression, options)
                End If
            Case QBuilder.ExpressionType.Logical
                UseQuotes = False
            Case ExpressionType.Exact
                Return Expression
        End Select

        Return PrepareExpressionCommon(Expression, WildLeft, WildRight, UseQuotes)
    End Function

    Private Shared Function PrepareExpressionCommon(ByVal Expression As String, ByVal WildLeft As Boolean, ByVal WildRight As Boolean, ByVal UseQuotes As Boolean) As String
        Dim ret As String = ""
        If UseQuotes Then ret &= "'"
        If WildLeft Then ret &= "%"

        If UseQuotes Then Expression = Expression.Replace("'", "''")
        If WildLeft OrElse WildRight Then Expression = Expression.Replace("%", "[%]")

        ret &= Expression

        If WildRight Then ret &= "%"
        If UseQuotes Then ret &= "'"

        Return ret
    End Function

    Public ReadOnly Property ShouldRender() As Boolean Implements ICriteriaComponent.HasContent
        Get
            Return True
        End Get
    End Property

    Private Sub CheckPatternAndOp()
        'If IsNumeric(RightExpression) AndAlso (mOp = Operation.Contains OrElse
        '                               mOp = Operation.EndsWith OrElse
        '                               mOp = Operation.StartsWith OrElse
        '                               mOp = Operation.Like OrElse
        '                               mOp = Operation.IsNotNULL OrElse
        '                               mOp = Operation.IsNULL) Then
        '    Throw New InvalidOperationException("Numeric Patterns are not valid with the selected operator.")
        'End If
    End Sub

    Private Function IsContainsStyleOperator(Op As Operation) As Boolean
        Select Case Op
            Case Operation.Contains, Operation.EndsWith, Operation.Like, Operation.StartsWith
                Return True
            Case Else
                Return False
        End Select
    End Function
End Class

Public Enum Operation As Integer
    LessThen = 0
    EqualTo
    GreaterThen
    NotEqualTo
    Contains
    StartsWith
    EndsWith
    IsNULL
    [Like]
    IsNotNULL
    LessThanEqualTo
    GreaterThanEqualTo
    NotLessThan
    NotGreaterThan
End Enum
